﻿namespace HostTracker.Azure.Management

open System
open System.Xml
open System.Net
open System.ServiceModel
open System.ServiceModel.Web
open System.Xml.Serialization
open System.Runtime.Serialization

(*
<?xml version="1.0" encoding="utf-8"?>
<Server xmlns=”http://schemas.microsoft.com/sqlazure/2010/12/”>
  <AdministratorLogin>MyAdminAccount</AdministratorLogin>
  <AdministratorLoginPassword>MyAdminPassword</AdministratorLoginPassword>
  <Location>North Central US | South Central US | North Europe | West Europe | East Asia | Southeast Asia</Location>
</Server>
*)
[<DataContract(Name="Server", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
[<XmlRootAttribute("Server", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type Server() =     
    let mutable login = ""
    let mutable password = ""
    let mutable location = ""
    [<DataMember(Name="AdministratorLogin", Order = 2)>]
    [<XmlElement("AdministratorLogin")>]
    member x.Login with get () = login and set v = login <- v
    [<DataMember(Name="AdministratorLoginPassword", Order = 3, IsRequired = false)>]
    [<XmlElement("AdministratorLoginPassword")>]
    member x.Password with get () = password and set v = password <- v
    [<DataMember(Name="Location", Order = 4)>]
    [<XmlElement("Location")>]
    member x.Location with get () = location and set v = location <- v

[<DataContract(Name="Server", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type ServerExtended() = 
    let mutable name = ""
    let mutable login = ""
    let mutable location = ""
    [<DataMember(Name="Name", Order = 1)>]
    member x.Name with get () = name and set v = name <- v
    [<DataMember(Name="AdministratorLogin", Order = 2)>]
    member x.Login with get () = login and set v = login <- v
    [<DataMember(Name="Location", Order = 3)>]
    member x.Location with get () = location and set v = location <- v
//[<DataContract(Name="ServerName", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
[<XmlRootAttribute("ServerName", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type ServerName() = 
    let mutable name = ""
    [<XmlTextAttribute>] member x.Name with get () = name and set v = name <- v

(*<Servers xmlns="http://schemas.microsoft.com/sqlazure/2010/12/">
  <Server>
    <Name>ServerName</Name>
    <AdministratorLogin>AdminAccount</AdministratorLogin>
    <Location>North Central US | South Central US | North Europe | West Europe | East Asia | Southeast Asia</Location>
  </Server>
</Servers>
*)
[<CollectionDataContract(Name="Servers", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type Servers() = inherit System.Collections.Generic.List<ServerExtended>()

(*
<?xml version="1.0" encoding="utf-8"?>
<AdministratorLoginPassword xmlns="http://schemas.microsoft.com/sqlazure/2010/12/">
  TheNewPassword
</AdministratorLoginPassword>
*)

[<XmlRootAttribute("AdministratorLoginPassword", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type PasswordChanging() =
    let mutable password = ""
    [<XmlTextAttribute>] member x.Password with get () = password and set v = password <- v
    
(*
<FirewallRules xmlns="http://schemas.microsoft.com/sqlazure/2010/12/">
  <FirewallRule>
    <Name>Firewall Rule Name</Name>
    <StartIpAddress>Start IP Addesss Range</StartIpAddress>
    <EndIpAddress>End IP address Range</EndIpAddress>
  </FirewallRule>
</FirewallRules>
*)
[<DataContract(Name="FirewallRule", Namespace="http://schemas.microsoft.com/sqlazure/2010/12/")>]
type FirewallRule() =    
    let mutable startIpAddress = ""
    let mutable endIpAddress = ""
    [<DataMember(Name="StartIpAddress", Order = 2)>]
    member x.StartIpAddress with get () = startIpAddress and set v = startIpAddress <- v
    [<DataMember(Name="EndIpAddress", Order = 3)>]
    member x.EndIpAddress with get () = endIpAddress and set v = endIpAddress <- v

[<DataContract(Name="FirewallRule", Namespace="http://schemas.microsoft.com/sqlazure/2010/12/")>]
type FirewallRuleExtended() =
    let mutable startIpAddress = ""
    let mutable endIpAddress = ""
    let mutable name = ""
    [<DataMember(Name="Name", Order = 1)>]
    member x.Name with get() = name and set v = name <- v
    [<DataMember(Name="StartIpAddress", Order = 2)>]
    member x.StartIpAddress with get () = startIpAddress and set v = startIpAddress <- v
    [<DataMember(Name="EndIpAddress", Order = 3)>]
    member x.EndIpAddress with get () = endIpAddress and set v = endIpAddress <- v

[<CollectionDataContract(Name="FirewallRules", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type FirewallRules() =
    inherit System.Collections.Generic.List<FirewallRuleExtended>()

(*
<IpAddress xmlns=http://schemas.microsoft.com/sqlazure/2010/12/>The Auto Detected IP Address</IpAddress>
*)

[<XmlRootAttribute("IpAddress", Namespace = "http://schemas.microsoft.com/sqlazure/2010/12/")>]
type DetectedIPAddress() = 
    let mutable address = ""
    [<XmlTextAttribute>]
    member x.Address with get () = address and set v = address <- v
    
[<Interface>]
[<ServiceContractAttribute>]
type ISqlManagement =    
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers", Method="POST")>]
    [<XmlSerializerFormatAttribute>]
    abstract CreateServer: server:Server -> ServerName 
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginCreateServer: server:Server*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndCreateServer: result:IAsyncResult -> ServerName
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers", Method="GET")>]
    abstract GetServers: unit -> Servers
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginGetServers: callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndGetServers: result:IAsyncResult -> Servers
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}", Method="DELETE")>]
    abstract DeleteServer: serverName:string -> unit
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginDeleteServer: serverName:string*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndDeleteServer: result:IAsyncResult -> unit
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}?op=ResetPassword", Method="POST")>]
    [<XmlSerializerFormatAttribute>]
    abstract ResetPassword: serverName:string * newPassword:PasswordChanging -> unit    
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginResetPassword: serverName:string*newPassword:PasswordChanging*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndResetPassword: result:IAsyncResult -> unit
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}/firewallrules", Method="GET")>]
    abstract GetFirewallRules: serverName:string -> FirewallRules
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginGetFirewallRules: serverName:string*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndGetFirewallRules: result:IAsyncResult -> FirewallRules
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}/firewallrules/{ruleName}", Method="PUT")>]    
    abstract CreateFirewallRule: serverName:string * ruleName:string * rule:FirewallRule -> unit
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginCreateFirewallRule: serverName:string*ruleName:string*rule:FirewallRule*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndCreateFirewallRule: result:IAsyncResult -> unit
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}/firewallrules/{ruleName}?op=AutoDetectClientIP", Method="POST")>]
    [<XmlSerializerFormatAttribute>]
    abstract CreateFirewallRuleWithAutoDetect: serverName:string*ruleName:string  -> DetectedIPAddress
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginCreateFirewallRuleWithAutoDetect: serverName:string*ruleName:string*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndCreateFirewallRuleWithAutoDetect: result:IAsyncResult -> DetectedIPAddress
    [<OperationContract>]
    [<WebInvoke(UriTemplate="/servers/{serverName}/firewallrules/{ruleName}", Method="DELETE")>]
    abstract DeleteFirewallRule: serverName:string * ruleName:string -> unit
    [<OperationContract(AsyncPattern = true)>]
    abstract BeginDeleteFirewallRule: serverName:string*ruleName:string*callback:AsyncCallback*state:obj -> IAsyncResult
    abstract EndDeleteFirewallRule: result:IAsyncResult -> unit
    
module SqlManagement = 
    let private sync withBody func = 
        use x = new BaseManagement<ISqlManagement>(true)
        x.Sync<_, SqlManagementError> withBody "1.0" func
    let private asyncBegin (x: BaseManagement<ISqlManagement>) withBody beginFunc = 
        x.AsyncBegin withBody "1.0" beginFunc
    let private asyncEnd (x: BaseManagement<ISqlManagement>) func = 
        x.AsyncEnd<_, SqlManagementError> func
    let CreateServer (server: Server) = 
        sync true (fun channel -> channel.CreateServer(server))
    let CreateServerAsync (server: Server) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x true (fun channel (rc, s) -> channel.BeginCreateServer(server, rc, s)),
            asyncEnd x (fun channel -> channel.EndCreateServer))
        }
    let CreateServerTask(server) = CreateServerAsync(server) |> Async.StartAsTask
    let GetServers () =
        sync false (fun channel -> channel.GetServers())
    let GetServersAsync() = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x false (fun channel -> channel.BeginGetServers),
            asyncEnd x (fun channel -> channel.EndGetServers))
        }
    let GetServerTask = GetServersAsync >> Async.StartAsTask
    let DeleteServer(serverName:string) =
        sync false (fun channel -> channel.DeleteServer(serverName))
    let DeleteServerAsync(serverName: string) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x false (fun channel (rc, s) -> channel.BeginDeleteServer(serverName, rc, s)),
            asyncEnd x (fun channel -> channel.EndDeleteServer))
        }
    let DeleteServerTask = DeleteServerAsync >> Async.StartAsTask
    let ResetPassword(serverName:string, newPassword: string) =
        sync true (fun channel -> channel.ResetPassword(serverName, PasswordChanging(Password = newPassword)))
    let ResetPasswordAsync(serverName:string, newPassword: string) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x true (fun channel (rc, s) -> channel.BeginResetPassword(serverName, PasswordChanging(Password = newPassword), rc, s)),
            asyncEnd x (fun channel -> channel.EndResetPassword))
        }
    let ResetPasswordTask = ResetPasswordAsync >> Async.StartAsTask
    let GetFirewallRules (serverName) =
        sync false (fun channel -> channel.GetFirewallRules(serverName))
    let GetFirewallRulesAsync (serverName) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return Async.FromBeginEnd(
            asyncBegin x false (fun channel (rc, s) -> channel.BeginGetFirewallRules(serverName, rc, s)),
            asyncEnd x (fun channel -> channel.EndGetFirewallRules))
        }
    let GetFirewallRulesTask = GetFirewallRulesAsync >> Async.StartAsTask
    let CreateFirewallRule (serverName, ruleName, rule) =
        sync true (fun channel -> channel.CreateFirewallRule(serverName, ruleName, rule))
    let CreateFirewallRuleAsync (serverName, ruleName, rule) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return Async.FromBeginEnd(
            asyncBegin x true (fun channel (rc, s) -> channel.BeginCreateFirewallRule(serverName, ruleName, rule, rc, s)),
            asyncEnd x (fun channel -> channel.EndCreateFirewallRule))
        }
    let CreateFirewallRuleTask (serverName, ruleName, rule) = CreateFirewallRuleAsync(serverName, ruleName, rule) |> Async.StartAsTask
    let CreateFirewallRuleWithAutoDetect (serverName, ruleName) =
        sync false (fun channel -> channel.CreateFirewallRuleWithAutoDetect(serverName, ruleName))
    let CreateFirewallRuleWithAutoDetectAsync (serverName, ruleName) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x false (fun channel (rc, s) -> channel.BeginCreateFirewallRuleWithAutoDetect(serverName, ruleName, rc, s)),
            asyncEnd x (fun channel -> channel.EndCreateFirewallRuleWithAutoDetect))
        }
    let CreateFirewallRuleWithAutoDetectTask = CreateFirewallRuleWithAutoDetectAsync >> Async.StartAsTask
    let DeleteFirewallRule(serverName, ruleName) =
        sync false (fun channel -> channel.DeleteFirewallRule(serverName, ruleName))         
    let DeleteFirewallRuleAsync(serverName, ruleName) = async {
        use x = new BaseManagement<ISqlManagement>(true)
        return! Async.FromBeginEnd(
            asyncBegin x false (fun channel (rc, s) -> channel.BeginDeleteFirewallRule(serverName, ruleName, rc, s)),
            asyncEnd x (fun channel -> channel.EndDeleteFirewallRule))
        }
    let DeleteFirewallRuleTask = DeleteFirewallRuleAsync >> Async.StartAsTask       